package com.rzgeo.rzmaplib.da;

import android.content.Context;
import android.content.DialogInterface;
import android.util.SparseArray;
import android.widget.ArrayAdapter;
import android.widget.ListView;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONWriter;
import com.esri.arcgisruntime.data.Field;
import com.linuxense.javadbf.DBFDataType;
import com.linuxense.javadbf.DBFField;
import com.linuxense.javadbf.DBFReader;
import com.linuxense.javadbf.DBFRow;
import com.linuxense.javadbf.DBFUtils;
import com.linuxense.javadbf.DBFWriter;
import com.rzgeo.rzmaplib.R;
import com.rzgeo.rzmaplib.util.RmlUtil;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import static android.widget.AbsListView.CHOICE_MODE_MULTIPLE;

public class RmlStdShpNode {

    public static class ShpDefBean{
        public boolean editable;
        public String alase;

        public ShpDefBean(boolean editable, String alias) {
            this.editable = editable;
            this.alase = alias;
        }

    }

    public static class StdFields{
        public static Field UID= Field.createString("RXUID","唯一编码",25);
        public static Field Code1= Field.createString("RXCODE1","一级编码",25);
        public static Field Code2= Field.createString("RXCODE2","二级编码",25);
        public static Field Code3= Field.createString("RXCODE3","三级编码",25);
        public static Field Code4= Field.createString("RXCODE4","四级编码",25);
        public static Field ExLabel1= Field.createString("RXEXLAB1","扩展属性1",50);
        public static Field ExLabel2= Field.createString("RXEXLAB2","扩展属性2",50);
        public static Field ExLabel3= Field.createString("RXEXLAB3","扩展属性3",50);
        public static Field ExLabel4= Field.createString("RXEXLAB4","扩展属性4",50);
        public static Field Remark=Field.createString("RXREMARK","备注",25);
        private static Field[] __fields=null;
        private static Map<String,Field> __fieldMap;


        public static Field[]getStdFields(){
            if(__fields==null){
                __fields=new Field[]{UID,Code1,Code2,Code3,Code4,ExLabel1,ExLabel2,ExLabel3,ExLabel4,Remark};
            }
            return __fields;
        }
        public static Map<String,Field>getStdFieldMap(){
            if(__fieldMap==null){
                __fieldMap= new HashMap<String,Field>(){{
                    put(UID.getName(),UID);
                    put(Code1.getName(),Code1);
                    put(Code2.getName(),Code2);
                    put(Code3.getName(),Code3);
                    put(Code4.getName(),Code4);
                    put(ExLabel1.getName(),ExLabel1);
                    put(ExLabel2.getName(),ExLabel2);
                    put(ExLabel3.getName(),ExLabel3);
                    put(ExLabel4.getName(),ExLabel4);
                    put(Remark.getName(),Remark);
                }};
            }
            return __fieldMap;

        }
    }

    public static class ShpFilePart{
        public final File shpFile;//.shp — 图形格式，用于保存元素的几何实体。
        public final File shxFile;//.shx — 图形索引格式。几何体位置索引，记录每一个几何体在shp文件之中的位置，能够加快向前或向后搜索一个几何体的效率。
        public File dbfFile;//.dbf — 属性数据格式，以dBase IV的数据表格式存储每个几何形状的属性数据
        public final File prjFile;//.prj — 投影格式，用于保存地理坐标系统与投影信息，是一个存储well-known text投影描述符的文本文件。

        public final File sbnFile;//.sbn and .sbx — 几何体的空间索引
        public final File sbxFile;
        public final File fbnFile;//.fbn and .fbx — 只读的Shapefiles的几何体的空间索引
        public final File fbxFile;
        public final File ainFile;//.ain and .aih — 列表中活动字段的属性索引。
        public final File aihFile;
        public final File ixsFile;//.ixs — 可读写Shapefile文件的地理编码索引
        public final File mxsFile;//.mxs — 可读写Shapefile文件的地理编码索引(ODB格式)
        public final File atxFile;//.atx — .dbf文件的属性索引，其文件名格式为shapefile.columnname.atx (ArcGIS 8及之后的版本)
        public final File shpXmlFile;//.shp.xml — 以XML格式保存元数据。
        public final File cpgFile;//.cpg — 用于描述.dbf文件的代码页，指明其使用的字符编码
        public final File RxExdef;//.cpg — 用于描述.dbf文件的代码页，指明其使用的字符编码
        public final File RxLabel;//.cpg — 用于描述.dbf文件的代码页，指明其使用的字符编码
        public final File RxRender;//.cpg — 用于描述.dbf文件的代码页，指明其使用的字符编码

        public ShpFilePart(String mainFilePath){
            shpFile =new File(mainFilePath);
            String name = RmlUtil.Files.getFileName(shpFile);
            String pDir = shpFile.getParent();

            String fs = File.separator;


            shxFile=new File(pDir+fs+name+".shx");
            dbfFile=new File(pDir+fs+name+".dbf");

            prjFile=new File(pDir+fs+name+".prj");
            sbnFile=new File(pDir+fs+name+".sbn");
            sbxFile=new File(pDir+fs+name+".sbx");
            fbnFile=new File(pDir+fs+name+".fbn");
            fbxFile=new File(pDir+fs+name+".fbx");
            ainFile=new File(pDir+fs+name+".ain");
            aihFile=new File(pDir+fs+name+".aih");


            ixsFile=new File(pDir+fs+name+".ixs");
            mxsFile    =new File(pDir+fs+name+".mxs");
            atxFile    =new File(pDir+fs+name+".atx");

            shpXmlFile  =new File(pDir+fs+name+".shp.xml");
            cpgFile    =new File(pDir+fs+name+".cpg");

            RxExdef=new File(pDir+fs+name+".rxdef");
            RxLabel=new File(pDir+fs+name+".rxlable");
            RxRender=new File(pDir+fs+name+".rxrender");

        }
    }

    private static class ShpFileCheckResult{
        ArrayList<Field>field2Add=new ArrayList<>();
        ArrayList<DBFField>field2Exsit=new ArrayList<>();
        ShpFilePart shpFilePart;
        ShpDefBean ShpDefBean;


        public ShpFileCheckResult(ShpFilePart shpFilePart) {
            this.shpFilePart=shpFilePart;
        }
    }

    public static boolean checkShpForEdit(Context context,File[] shps) {

        Map<String, Field> fm = new HashMap<>(StdFields.getStdFieldMap());
        ArrayList<ShpFileCheckResult>shpCheckResults=new ArrayList<>();
        int count=0;
        for(File shp:shps){
            DBFReader reader=null;
            ShpFilePart shpFilePart=new ShpFilePart(shp.getAbsolutePath());
            ShpFileCheckResult shpCheckResult=new ShpFileCheckResult(shpFilePart);
            try {
                reader =new DBFReader(new FileInputStream(shpFilePart.dbfFile),java.nio.charset.StandardCharsets.UTF_8);

                if(shpFilePart.RxExdef.isFile()&&shpFilePart.RxExdef.exists()){

                    try {
                        ShpDefBean bean=JSON.parseObject(
                                new FileInputStream(shpFilePart.RxExdef),
                                Charset.forName("UTF-8"),
                                ShpDefBean.class);
                        if(bean!=null){
                            shpCheckResult.ShpDefBean=bean;

                            if(!bean.editable){
                                break;
                            }
                        }
                    }catch (Exception e){
                        RmlUtil.Log.Err(e);
                    }
                }
                int numberOfFields = reader.getFieldCount();
                for (int i = 0; i < numberOfFields; i++)
                {
                    DBFField field = reader.getField(i);
                    String name = field.getName();
                    if(name!=null)
                        name=name.toUpperCase();

                    if(fm.containsKey(name)){
                        fm.remove(name);
                    }
                    shpCheckResult.field2Exsit.add(field);
                }

                for (String name :fm.keySet()) {
                    shpCheckResult.field2Add.add(fm.get(name));
                    count++;
                }
            }catch (Exception e){
                RmlUtil.Log.Err(e);
                return false;
            }finally {
                DBFUtils.close(reader);
                shpCheckResults.add(shpCheckResult);
            }
        }

        if(count>0){
            Dialog.ShowShpConfDlg(context, shpCheckResults, new Dialog.OnFinish() {
                @Override
                public void onCancel() {
                    RmlUtil.Tip.Toast("取消设置");
                }

                @Override
                public void onFailed() {
                    RmlUtil.Tip.Toast("设置失败");
                }

                @Override
                public void onFinish() {
                    RmlUtil.Tip.Toast("设置完成");
                }
            });
            return false;
        }else
            return true;

    }

    private static class Dialog{

        interface OnFinish extends RmlUtil.Dialog.OnFinish{
            void onCancel();
            void onFailed();
        }

        private static void ShowShpConfDlg(Context context, final ArrayList<ShpFileCheckResult>shpCheckResults, final OnFinish onFinish){
//        RmlUtil.Dialog.List();

            if(shpCheckResults==null)
                return;


            String[]items=new String[shpCheckResults.size()];
            for(int i=0;i<shpCheckResults.size();i++){
                ShpFileCheckResult r = shpCheckResults.get(i);
                String msg = "未设置";
                if(r.ShpDefBean!=null)
                    msg=(r.ShpDefBean.editable)?"编辑":"只读";
                items[i]=r.shpFilePart.shpFile+"\t\t"+msg;
            }

            final ListView listView = new ListView(context);

            listView.setMinimumWidth(800);
            listView.setMinimumHeight(600);
            listView.setAdapter(
                    new ArrayAdapter<>(
                            context, android.R.layout.simple_list_item_multiple_choice, items));

            listView.setItemsCanFocus(false);
            listView.setChoiceMode(CHOICE_MODE_MULTIPLE);

            android.support.v7.app.AlertDialog.Builder dlg =
                    new android.support.v7.app.AlertDialog.Builder(context, R.style.appalertdialog)
                            .setIcon(android.R.drawable.ic_menu_edit)
                            .setView(listView)
                            .setTitle("请勾选需要编辑的图层，未勾选将被设置为\"只读\"")
                            .setPositiveButton("设置", new DialogInterface.OnClickListener() {
                                public void onClick(DialogInterface dialog, int which) {
                                    doUpDateShpSchemeAndConf(listView,shpCheckResults);
                                    onFinish.onFinish();
                                }
                            })
                            .setNegativeButton("取消", new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    onFinish.onCancel();
                                }
                            });


            dlg.show();
        }

        public static void doUpDateShpSchemeAndConf(ListView listView, ArrayList<ShpFileCheckResult> shpCheckResults) {

            for (int i = 0; i < listView.getCount(); i++)
            {
                boolean editable = listView.isItemChecked(i);

                ShpFileCheckResult scr = shpCheckResults.get(i);

                File fdbfOld = shpCheckResults.get(i).shpFilePart.dbfFile;
                File fdbfNew = new File(fdbfOld.getParent()+File.separator+RmlUtil.Files.fileName(fdbfOld)+"_2.dbf");

                try {
                    if(fdbfNew.exists())
                        fdbfNew.delete();
                    fdbfNew.createNewFile();
                } catch (IOException e) {
                    RmlUtil.Log.Err(e);
                }

                if(editable&&scr.field2Add.size()>0){
                DBFWriter writer=null;
                    try {
                        writer = new DBFWriter(new FileOutputStream(fdbfOld));
                        createNewDbf(scr, writer);
//                        writer = new DBFWriter(new FileOutputStream(fdbfNew));
//                        DBFField[] fields = createNewDbf(scr, writer);
//                        writeRecode(fdbfOld,scr.field2Exsit,writer,fields);
//                        DBFUtils.close(writer);

                        shpCheckResults.get(i).shpFilePart.dbfFile.delete();
                        fdbfNew.renameTo(shpCheckResults.get(i).shpFilePart.dbfFile);
                        shpCheckResults.get(i).shpFilePart.dbfFile=fdbfNew;
                    }catch (Exception e){
                        RmlUtil.Log.Err(e);
                        DBFUtils.close(writer);
                    }finally {
                    }
                }

                File fdef = shpCheckResults.get(i).shpFilePart.RxExdef;
                if(scr.ShpDefBean==null){
                    scr.ShpDefBean=new ShpDefBean(
                            editable,
                            RmlUtil.Files.getFileName(scr.shpFilePart.shpFile));

                    if(!fdef.exists()) {
                        try {
                            fdef.createNewFile();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }else{
                    scr.ShpDefBean.editable=editable;
                }
                JSONWriter jw=null;
                try {
                    jw = new JSONWriter(new FileWriter(fdef));
                    jw.startObject();
                    jw.writeObject(scr.ShpDefBean);
                    jw.flush();
                } catch (IOException e) {
                    RmlUtil.Log.Err(e);
                }finally {
                    if(jw!=null) {
                        try {
                            jw.close();
                        } catch (IOException e) {
                            RmlUtil.Log.Err(e);
                        }
                    }
                }
            }
        }

        private static DBFField[] createNewDbf(ShpFileCheckResult scr, DBFWriter writer) {

            DBFField[]fields=new DBFField[scr.field2Exsit.size()+scr.field2Add.size()];
            for (int m = 0; m < scr.field2Exsit.size(); m++) {
                fields[m]=scr.field2Exsit.get(m);
            }

            for (int j = 0; j < scr.field2Add.size(); j++) {
                Field f2a=scr.field2Add.get(j);

                String n = f2a.getName();
                DBFField field = new DBFField();

                switch (f2a.getFieldType()){
                    case SHORT:
                        field=new DBFField(n,DBFDataType.NUMERIC,5,0);
                        break;
                    case INTEGER:
                        field=new DBFField(n,DBFDataType.NUMERIC,10,0);
                        break;
                    case FLOAT:
                        field=new DBFField(n,DBFDataType.FLOATING_POINT,13,11);
                        break;
                    case DOUBLE:
                        field=new DBFField(n,DBFDataType.FLOATING_POINT,19,11);
                        break;
                    case DATE:
                        field=new DBFField(n,DBFDataType.DATE,8);
                        break;
                    default:
                        int l = f2a.getLength();
                        field=new DBFField(n,DBFDataType.CHARACTER,l);
                        break;
                }

                fields[j+scr.field2Exsit.size()]=field;
            }
            writer.setFields(fields);
            return fields;
        }

        private static DBFField[] createNewDbf2(ShpFileCheckResult scr, DBFWriter writer) {

            DBFField[]fields=new DBFField[scr.field2Exsit.size()+scr.field2Add.size()];
            for (int m = 0; m < scr.field2Exsit.size(); m++) {
                fields[m]=scr.field2Exsit.get(m);
            }

            for (int j = 0; j < scr.field2Add.size(); j++) {
                Field f2a=scr.field2Add.get(j);

                String n = f2a.getName();
                DBFField field = new DBFField();

                switch (f2a.getFieldType()){
                    case SHORT:
                        field=new DBFField(n,DBFDataType.NUMERIC,5,0);
                        break;
                    case INTEGER:
                        field=new DBFField(n,DBFDataType.NUMERIC,10,0);
                        break;
                    case FLOAT:
                        field=new DBFField(n,DBFDataType.FLOATING_POINT,13,11);
                        break;
                    case DOUBLE:
                        field=new DBFField(n,DBFDataType.FLOATING_POINT,19,11);
                        break;
                    case DATE:
                        field=new DBFField(n,DBFDataType.DATE,8);
                        break;
                    default:
                        int l = f2a.getLength();
                        field=new DBFField(n,DBFDataType.CHARACTER,l);
                        break;
                }

                fields[j+scr.field2Exsit.size()]=field;
            }
            writer.setFields(fields);
            return fields;
        }

        private static void writeRecode(File fdbfOld, ArrayList<DBFField> field2Exsit, DBFWriter writer, DBFField[] fields) {

            DBFReader reader = null;
            try {

                reader =new DBFReader(new FileInputStream(fdbfOld),java.nio.charset.StandardCharsets.UTF_8);
                Object[][] row2Add = new Object[reader.getRecordCount()][fields.length];
                DBFRow row;


                int[]iSourceField=new int[field2Exsit.size()];
                DBFDataType[]iSourceFieldType=new DBFDataType[field2Exsit.size()];
                int[]iTargetField=new int[field2Exsit.size()];
                SparseArray<String>iTargetFieldSet=new SparseArray<>(field2Exsit.size());
                int[]iBlankField=new int[fields.length-field2Exsit.size()];

                for (int i = 0; i < field2Exsit.size(); i++) {
                    DBFField fe = field2Exsit.get(i);
                    for (int m = 0; m < reader.getFieldCount(); m++) {
                        if(reader.getField(m).getName().equals(fe.getName())){
                            iSourceField[i]=m;
                            iSourceFieldType[i]=reader.getField(m).getType();
                            break;
                        }
                    }
                    for (int n = 0; n < fields.length; n++) {
                        if(fields[n].getName().equals(fe.getName())){
                            iTargetField[i]=n;
                            iTargetFieldSet.put(n,"T");
                            break;
                        }
                    }
                }

                for (int p = 0,q=0; p < fields.length; p++) {
                    if(iTargetFieldSet.indexOfKey(p)>-1)
                        continue;
                    iBlankField[q++]=p;
                }



                int i=0;
                while ((row = reader.nextRow()) != null) {
                    for (int d = 0; d < iBlankField.length; d++) {
                        row2Add[i][iBlankField[d]]=null;
                    }
                    for (int e = 0; e < iTargetField.length; e++) {
                        Object v = null;
                        int fn=iSourceField[e];
                        switch (iSourceFieldType[e]){
                            case LONG:
                                v=row.getLong(fn);
                                break;
                            case FLOATING_POINT:
                                v=row.getFloat(fn);
                                break;
                            case DOUBLE:
                                v=row.getDouble(fn);
                                break;
                            case DATE:
                                v=row.getDate(fn);
                                break;
                            default:// CHARACTER:
                                v=row.getString(fn);
                                break;
                        }
                        row2Add[i][iTargetField[e]]=v;
                    }
                    i++;
                }

                for (int m = 0; m < row2Add.length; m++) {
                    writer.addRecord(row2Add[m]);
                }
            }catch (Exception e){
                RmlUtil.Log.Err(e);
            }finally {
                DBFUtils.close(reader);
            }
        }

        private static void writeRecode2(File fdbfOld, ArrayList<DBFField> field2Exsit, DBFWriter writer, DBFField[] fields) {

            DBFReader reader = null;
            try {

                reader =new DBFReader(new FileInputStream(fdbfOld),java.nio.charset.StandardCharsets.UTF_8);
                Object[][] row2Add = new Object[reader.getRecordCount()][fields.length];
                DBFRow row;


                int[]iSourceField=new int[field2Exsit.size()];
                DBFDataType[]iSourceFieldType=new DBFDataType[field2Exsit.size()];
                int[]iTargetField=new int[field2Exsit.size()];
                SparseArray<String>iTargetFieldSet=new SparseArray<>(field2Exsit.size());
                int[]iBlankField=new int[fields.length-field2Exsit.size()];

                for (int i = 0; i < field2Exsit.size(); i++) {
                    DBFField fe = field2Exsit.get(i);
                    for (int m = 0; m < reader.getFieldCount(); m++) {
                        if(reader.getField(m).getName().equals(fe.getName())){
                            iSourceField[i]=m;
                            iSourceFieldType[i]=reader.getField(m).getType();
                            break;
                        }
                    }
                    for (int n = 0; n < fields.length; n++) {
                        if(fields[n].getName().equals(fe.getName())){
                            iTargetField[i]=n;
                            iTargetFieldSet.put(n,"T");
                            break;
                        }
                    }
                }

                for (int p = 0,q=0; p < fields.length; p++) {
                    if(iTargetFieldSet.indexOfKey(p)>-1)
                        continue;
                    iBlankField[q++]=p;
                }



                int i=0;
                while ((row = reader.nextRow()) != null) {
                    for (int d = 0; d < iBlankField.length; d++) {
                        row2Add[i][iBlankField[d]]=null;
                    }
                    for (int e = 0; e < iTargetField.length; e++) {
                        Object v = null;
                        int fn=iSourceField[e];
                        switch (iSourceFieldType[e]){
                            case LONG:
                                v=row.getLong(fn);
                                break;
                            case FLOATING_POINT:
                                v=row.getFloat(fn);
                                break;
                            case DOUBLE:
                                v=row.getDouble(fn);
                                break;
                            case DATE:
                                v=row.getDate(fn);
                                break;
                            default:// CHARACTER:
                                v=row.getString(fn);
                                break;
                        }
                        row2Add[i][iTargetField[e]]=v;
                    }
                    i++;
                }

                for (int m = 0; m < row2Add.length; m++) {
                    writer.addRecord(row2Add[m]);
                }
            }catch (Exception e){
                RmlUtil.Log.Err(e);
            }finally {
                DBFUtils.close(reader);
            }
        }
    }


}
